package com.zf.validation.controller;

import com.zf.common.core.result.RestResponse;
import com.zf.validation.entity.GroupEntity;
import com.zf.validation.entity.NestParentEntity;
import com.zf.validation.entity.UserInfo;
import com.zf.validation.utils.ValidationList;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 校验样例
 */
@Validated // @Validated 注解只针对单个字段校验生效，对于bean对象的校验不生效
// @Valid //注解是无法对单个参数校验生效的无效的
@RestController
@RequestMapping("/testValid")
public class ValidateTestController {

    @Autowired
    private Validator validator;


    /**
     * 单个字段校验，必须要在类上加 @Validated 注解才能生效，且类上的 @Validated 注解只针对单个字段校验生效。
     * 如果在类上加 @Valid 注解，也是无效的
     */
    @GetMapping("/singleField")
    public RestResponse<?> singleField(@NotBlank(message = "用户名不能为空") @RequestParam(value = "username", required = false) String username) {
        return RestResponse.success(username);
    }


    /**
     * 类上的 @Validated 注解只针对单个字段校验生效，对于bean对象的校验不生效，此次不会执行校验。
     * 必须在方法的bean参数前加 @Validated 注解才会生效, 加 @Valid注解是无效的
     */
    @PostMapping("/classValidated")
    public RestResponse<?> classValidated(@RequestBody UserInfo userInfo) {
        return RestResponse.success(userInfo);
    }


    /**
     * 一般的bean对象校验
     */
    @PostMapping("/beanValid")
    public RestResponse<UserInfo> beanValid(@Validated @RequestBody UserInfo userInfo) {
        return RestResponse.success(userInfo);
    }

    /**
     * 如果 @Validated 指定分组校验，只校验指定分组的字段，没有分组的字段则不校验。
     * 如果 @Validated 没有指定分组，则只校验没有分组的字段，指定分组的字段则不校验。
     */
    @PostMapping("/groupValid")
    public RestResponse<GroupEntity> groupValid(@Validated({GroupEntity.Update.class, GroupEntity.Insert.class}) @RequestBody GroupEntity groupEntity) {
        return RestResponse.success(groupEntity);
    }


    /**
     * 校验 List 本身，只能对List本身做校验，例如：长度、是否空等等，不能执行其内部元素的校验注解,
     * 必须在类上加 @Validated 注解才能生效，加在其他地方都不生效
     */
    @PostMapping("/validListSelf")
    public RestResponse<?> validListSelf(@NotEmpty(message = "集合不能为空") @Size(max = 1, message = "集合长度不能超过1") @RequestBody List<UserInfo> list) {
        return RestResponse.success(list);
    }

    /**
     * 校验 List 内部元素，直接使用List<UserInfo> 类型参数是不会执行内部元素的注解校验的，必须自定义list，通过嵌套校验实现对List内部元素的注解校验
     */
    @PostMapping("/validList")
    public RestResponse<?> validList(@Validated @RequestBody ValidationList<UserInfo> userInfos) {
        return RestResponse.success(userInfos);
    }

    /**
     * 嵌套检验，一般只执行对象中的第一层级的注解校验，如果想要执行内部嵌套对象的注解校验，需要在被嵌套的对象上添加 @Valid 注解
     */
    @PostMapping("/validNest")
    public RestResponse<?> validNest(@Validated @RequestBody NestParentEntity nestParentEntity) {
        return RestResponse.success(nestParentEntity);
    }

    /**
     * 编程式校验，也就是手动校验，校验  POJO 类型对象
     */
    @PostMapping("/programValidPojo")
    public RestResponse<?> programValidPojo(@RequestBody GroupEntity groupEntity) {
        // 手动校验 POJO 类型对象，校验整个 Pojo 对象中的所有包含校验注解属性，如果有分组参数，则按分组情况执行属性上属于该分组的所有校验
        // validator.validate()只能校验Pojo对象，validator.validate(String/Integer) 这种基本类型校验是无效的
        Set<ConstraintViolation<GroupEntity>> violationSet = validator.validate(groupEntity, GroupEntity.Insert.class);
        // Set<ConstraintViolation<GroupEntity>> violationSet = validator.validate(groupEntity, GroupEntity.Update.class, GroupEntity.Insert.class);
        // 处理错误信息
        ArrayList<String> errList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(violationSet)) {
            for (ConstraintViolation<GroupEntity> violation : violationSet) {
                String message = violation.getMessage(); //错误信息
                String methodField = violation.getPropertyPath().toString(); // 校验字段名
                errList.add(methodField + ": " + message);
            }
        }
        if (CollectionUtils.isNotEmpty(errList)) {
            return RestResponse.failedWithData(errList);
        }
        return RestResponse.success(groupEntity);
    }

    /**
     * 编程式校验，也就是手动校验，只校验  POJO 类型对象 中的某个属性
     */
    @PostMapping("/programValidOnePropertyInPojo")
    public RestResponse<?> programValidOnePropertyInPojo(@RequestBody GroupEntity groupEntity) {
        // 手动校验  POJO 类型对象 中的某个含有校验注解的属性，其他的属性都不校验，如果有分组参数，则按分组情况执行该属性上属于该分组的所有校验
        // validator.validateProperty()只能校验Pojo对象，像 validator.validateProperty(String/Integer) 这种基本类型校验是无效的
        Set<ConstraintViolation<GroupEntity>> violationSet = validator.validateProperty(groupEntity, "id", GroupEntity.Update.class);
        // Set<ConstraintViolation<GroupEntity>> violationSet = validator.validateProperty(groupEntity, "id", GroupEntity.Insert.class);
        // 处理错误信息
        ArrayList<String> errList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(violationSet)) {
            for (ConstraintViolation<GroupEntity> violation : violationSet) {
                String message = violation.getMessage(); //错误信息
                String methodField = violation.getPropertyPath().toString(); // 校验字段名
                errList.add(methodField + ": " + message);
            }
        }
        if (CollectionUtils.isNotEmpty(errList)) {
            return RestResponse.failedWithData(errList);
        }
        return RestResponse.success(groupEntity);
    }

    /**
     * 编程式校验，也就是手动校验，校验简单的基本类型值。
     * 如果是简单类型，则直接手写校验逻辑即可，没有必要使用 Validator，以下只是做一下演示
     */
    @GetMapping("/programValidSingleValue")
    public RestResponse<?> programValidSingleValue(@RequestParam(name = "un", required = false) String un) {
        // 把空字符串变成 null
        un = !StringUtils.hasText(un) ? null : un;

        // 手动校验  校验简单的基本类型值，如果有分组参数，则按分组情况执行该属性上属于该分组的所有校验
        // 还是需要依赖于一个类型，才能完成简单类型的校验，只不过不需要我们创建该类型的对象了
        Set<ConstraintViolation<GroupEntity>> violationSet = validator.validateValue(
                GroupEntity.class, // 使用哪个类型来完成校验
                "id", // 使用类的哪个属性上的校验注解来完成校验，必须是类中存在的属性，即便 要校验的值的类型 和 属性的类型 不一致也可以完成校验，因为只是使用属性上的校验注解
                un, // 要校验的值
                GroupEntity.Update.class // 分组校验
        );

        // 处理错误信息
        ArrayList<String> errList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(violationSet)) {
            for (ConstraintViolation<GroupEntity> violation : violationSet) {
                String message = violation.getMessage(); //错误信息
                String methodField = violation.getPropertyPath().toString(); // 校验字段名
                errList.add(methodField + ": " + message);
            }
        }
        if (CollectionUtils.isNotEmpty(errList)) {
            return RestResponse.failedWithData(errList);
        }
        return RestResponse.success(un);
    }

}
